/**********************************************************************************
 * Code license:  GNU General Public License v3                                   *
 * Main page:     https://code.google.com/p/library-cross-platform/               *
 * Issue tracker: https://code.google.com/p/library-cross-platform/issues/list    *
***********************************************************************************/

package library.parser;

import java.io.*;
import java.lang.reflect.*;

import library.file.*;
import library.util.*;

@SuppressWarnings({ "rawtypes" })
public class AsciiParser extends ParserEvent {

  protected       static Hash          calcHash        = new Hash();
  protected final static String        EVENT_START     = "EVENT_START";
  protected final static String        EVENT_EXIT      = "EVENT_EXIT";
  protected final static long          eventStartHash  = calcHash.getStringHashValue(EVENT_START);
  protected final static long          eventExitHash   = calcHash.getStringHashValue(EVENT_EXIT);

  protected AdoptedAsciiReader  fileReader = null;

  public AsciiParser() {
    registrateEvent(EVENT_START, new EventStart().getClass().getName());
    registrateEvent(EVENT_EXIT,  new EventExit().getClass().getName());
  }
  
  public void setFile(String file) {
    this.setFile(new File(file));
  }
  
  public void setFile(File file) {
    fileReader = new AdoptedAsciiReader(file);
  }

  public static AsciiParser newInstance(Class parserClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException {
    String name = parserClass.getCanonicalName();
    AsciiParser parser = (AsciiParser)((AsciiParser)parserClass.newInstance());
    parser.registerClass(name, parser);
    return parser;    
  }

  public static AsciiParser newInstance(Class parserClass, Object notused) {
    AsciiParser parser = null;
    try {
      parser = AsciiParser.newInstance(parserClass);
    } catch(ClassNotFoundException e) {
      e.printStackTrace();
    } catch(InstantiationException e) {
      e.printStackTrace();
    }catch(IllegalAccessException e) {
      e.printStackTrace();
    }catch(InvocationTargetException e) {
      e.printStackTrace();
    }catch(SecurityException e) {
      e.printStackTrace();
    }catch(NoSuchMethodException e) {
      e.printStackTrace();
    }
    return parser;
  }


  public boolean startEventStates() throws InvocationTargetException, NoSuchMethodException, SecurityException {
    boolean             is_read_next_string = true;
    int                 read_file_result    = FILE_RESULT_OK;
    ParserEventListener eventListener;
    String              eventClassName;

    try {
      setNextEvent(EVENT_START);

      while(true) {
        if(is_read_next_string == true) {
          read_file_result = readRawData();

          if(read_file_result == FILE_RESULT_IO_EXCEPTION) {
            setNextEvent(EVENT_EXIT);
            is_read_next_string = false;
            continue;
          }

          if(read_file_result == FILE_RESULT_EOF) {
            setNextEvent(EVENT_EXIT);
            is_read_next_string = false;
            continue;
          } else if(read_file_result == FILE_RESULT_SIZE_ZERO) {
	         is_read_next_string = true;
             continue;
          }
        }

        if(isComment() != true) {
          eventClassName = getEventClassName(getNextEvent());
          eventListener = (ParserEventListener)getEventListener(eventClassName);
          eventListener.setParent(this);
          is_read_next_string = eventListener.start();

          if(calcHash.getStringHashValue(getNextEvent()) == eventExitHash) {
            eventClassName = getEventClassName(getNextEvent());
            eventListener = (ParserEventListener)getEventListener(eventClassName);
            eventListener.setParent(this);
            is_read_next_string = eventListener.start();
            break;
          }
        } else /* skip comments */ {
	      is_read_next_string = true;
        }
      } /* while() end */
    } catch(IllegalEventException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch(ClassNotFoundException e) {
      e.printStackTrace();
    }
    return false;
  }

  protected int readRawData(StringBuilder string) {
    int  result = FILE_RESULT_OK;
    char c      = '\0';

    string.setLength(0);

    try {
      while(true)
      {
        c = (char)fileReader.read();
        if(c == -1) {
          result = FILE_RESULT_EOF;
          return result;
        }

        if(c == '\n' || c == '\r') {
          c = (char)fileReader.read();
          if(c == '\n' || c == '\r')
            c = (char)fileReader.read();
          break;
        }
        else
         string.append(c);
      }
    } catch(IOException e) {
      e.printStackTrace();
      result = FILE_RESULT_IO_EXCEPTION;
    }
    return result;
  }

  protected int readRawData() {
    StringBuilder  raw_data         = new StringBuilder(80);
    int            read_file_result = readRawData(raw_data);

    if(read_file_result == FILE_RESULT_OK)
      setRawData(raw_data);

    return read_file_result;
  }

  protected boolean isComment() {
	return false;
  }

  protected boolean eos(char c) {
    return (c == '\n' || c == '\r' || c == '\0') ? true : false;
  }

  public int skipEmptyLines() {
    int                out        = FILE_RESULT_OK;
    StringBuilder      local_data = null;
    
    readRawData();
    local_data = this.getRawData();

    if(isEmptyString(local_data) == false) {
      do {
        out = readRawData(local_data);
      }while(out == FILE_RESULT_SIZE_ZERO && out != FILE_RESULT_EOF);
    }
    setRawData(local_data);
    return out;
  }

  public int skipEmptyData(StringBuilder raw_data, int index) {
    if(index >= raw_data.length())
     return index;

    /* skip ' ' and '\t' */
    while(raw_data.charAt(index) != '\r'    &&
          raw_data.charAt(index) != '\n'    &&
          raw_data.charAt(index) != '\0'    &&
          (raw_data.charAt(index) == ' ' || raw_data.charAt(index) == '\t'))
    {
      index ++;
    }
    return index;
  }

  public int skipEmptyData(String raw_data, int index) {
    if(index >= raw_data.length())
     return index;

    /* skip ' ' and '\t' */
    while(raw_data.charAt(index) != '\r'    &&
          raw_data.charAt(index) != '\n'    &&
          raw_data.charAt(index) != '\0'    &&
          (raw_data.charAt(index) == ' ' || raw_data.charAt(index) == '\t'))
    {
      index ++;
    }
    return index;
  }

  public int getDataFromString(StringBuilder raw_data, int raw_index, StringBuilder ret_data) {
    return getDataFromString(raw_data, raw_index, ',', ret_data);
  }

  public int getDataFromString(final StringBuilder raw_data, int raw_index, char separator, StringBuilder ret_data) {
    int data_len = raw_data.length();

    ret_data.setLength(0);
    raw_index = skipEmptyData(raw_data, raw_index);

    while((int)raw_index < data_len               &&
          raw_data.charAt(raw_index) != '\r'      &&
          raw_data.charAt(raw_index) != '\n'      &&
          raw_data.charAt(raw_index) != '\0'      &&
          raw_data.charAt(raw_index) != ' '       &&
          raw_data.charAt(raw_index) != '\t'      &&
          raw_data.charAt(raw_index) != separator)
    {
      ret_data.append(raw_data.charAt(raw_index));
      raw_index ++;
    }

    /* to avoid 'separator' for the next parameter */
    if(raw_index < data_len && raw_data.charAt(raw_index) == separator)
      raw_index ++;

    return raw_index;
  }

  public int getParamFromData(final StringBuilder raw_data, int raw_index, int paramid, StringBuilder ret_data) {
    return getParamFromData(raw_data, raw_index, ',', paramid, ret_data);
  }

  int getParamFromData(final StringBuilder raw_data, int raw_index, char separator, int paramid, final StringBuilder ret_data) {
    int local_count = 0;
    for(; local_count <= paramid; local_count++)
     raw_index = getDataFromString(raw_data, raw_index, separator, ret_data);
    return raw_index;
  }

  public StringBuilder parseDataParameter(final StringBuilder in, char c) {
    int           pos = 0;
    StringBuilder out = new StringBuilder();

    pos = skipEmptyData(in, 0);
    
    pos = findChar(in, pos, c);
    pos = pos + 1; /* skip separator 'c' */
    pos = skipEmptyData(in, pos);
    strcpytill(in, pos, '\n', out);

    return out;
  }

  public int findChar(final StringBuilder in, int pos, char c) {
    int index  = pos;
    int length = in.length();
    while(index < length && in.charAt(index) != c)
      index ++;

    return (index < length) ? index : -1;
  }

  /* parse string
   * IN:  string format: name_value:   data_value
   * OUT: out.name  = name_value
   *      out.value = data_value
   */
  public StringParamValue parseDataParameter(final StringBuilder in, int index, char separator) {
    StringParamValue out       = new StringParamValue();
    int              in_length = in.length();

    out.init();
    index = skipEmptyData(in, index);
    index = strcpytill(in, index, separator, out.name);
    
    if(out.name.length() == 0 || out.name.charAt(0) == '\0')
     out.name.setLength(0);

    if(index >= in_length) {
      out.value.setLength(0);
      return out;
    }

    index ++; /* skip separator */
    index = skipEmptyData(in, index);
    if(index >= in_length) {
      out.value.setLength(0);
      return out;
    }

    index = strcpytill(in, index, '\n', out.value);
    if(out.value.charAt(0) == '\0' || out.name.length() == 0)
     out.value.setLength(0);

    return out;
  }

  public int strcpytill(final StringBuilder in, int index, char till, StringBuilder out) {
    int length = in.length();
    if(index >= length)
     return -1;

    while(index < length           &&
          in.charAt(index) != till &&
          in.charAt(index) != '\n' &&
          in.charAt(index) != '\r' &&
          in.charAt(index) != '\0' )  {
      out.append(in.charAt(index++));
    }

    return index;
  }

  public boolean isEmptyString(final StringBuilder in) {
    if(in.length() == 0)
     return true;
    else
     return ( in.charAt(0) == '\n' || in.charAt(0) == '\r' || in.charAt(0) == '\0') ? true : false;
  }

  public void remove_space(StringBuilder str) {
    int length = str.length();
    int index = 0;
    while(index < length) {
     if(str.charAt(index) == ' ') {
       str.deleteCharAt(index);
       length = str.length();
     } else
       index ++;
    }
  }

  public void remove_eof_string(StringBuilder str) {
    int length = str.length();
    int index = 0;
    while(index < length) {
     if(str.charAt(index) == '\r' || str.charAt(index) == '\n')
       str.deleteCharAt(index);
    }
  }

  public boolean compareStrings(StringBuilder fs, StringBuilder ss) {
    int     fs_length   = fs.length();
    int     ss_length   = ss.length();
    int     half_length = ss_length / 2;
    int     index       = 0;
    boolean result      = true;

    if(fs_length != ss_length)
     return false;

    while(index < half_length) {
      if( (fs.charAt(index) != ss.charAt(index)) &&
          (fs.charAt(ss_length - index -1) != ss.charAt(ss_length - index -1)) ) {
        result = false;
        break;
      }
      index ++;
    }
    
    if(result == true && (fs_length & 0x01) == 1) {
      if(fs.charAt(half_length) != ss.charAt(half_length))
       result = false;
    }
    return result;
  }

  public class EventStart extends ParserEventListener {
    public EventStart() {}
    public boolean start() throws IllegalEventException {
      System.out.println("AsciiParser.EventStart.parse(...)");
      parent.setNextEvent(EVENT_EXIT);
      return false;
    }
  }

  public class EventExit extends ParserEventListener {
    public EventExit() {}
    public boolean start() throws IllegalEventException {
      System.out.println("AsciiParser.EventExit.parse(...)");
      parent.setNextEvent(EVENT_EXIT);
      return false;
    }
  }
}
